package com.liuwei.msmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.liuwei.msmall.cart.feign.ProductFeignService;
import com.liuwei.msmall.cart.feign.WareFeignService;
import com.liuwei.msmall.cart.interceptor.CartInterceptor;
import com.liuwei.msmall.cart.service.CartService;
import com.liuwei.msmall.cart.to.UserInfoTo;
import com.liuwei.msmall.cart.vo.CartItemVo;
import com.liuwei.msmall.cart.vo.CartVo;
import com.liuwei.msmall.cart.vo.SkuInfoVo;
import com.liuwei.msmall.common.constant.CartConstant;
import com.liuwei.msmall.common.dto.SkuStockTo;
import com.liuwei.msmall.common.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @Classname CartServiceImpl
 * @Description TODO
 * @Date 2020/6/28 12:52
 * @Created by 刘
 */
@Service
public class CartServiceImpl implements CartService {



    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    ProductFeignService productFeignService;
    @Autowired
    ThreadPoolExecutor executor;
    @Autowired
    WareFeignService wareFeignService;


    //将商品添加到购物车中
    @Override
    public CartItemVo addCart(Long skuId, Integer count) throws ExecutionException, InterruptedException {

        //拿到对redis对cart操作的对象
        BoundHashOperations<String, String, Object> cartOps = getCartOps();
        //先判断redis中购物车中是否已经添加了此商品信息
        Object cartSkuInfo =cartOps.get(skuId.toString());
        if (cartSkuInfo==null){
            //如果为空，说明之前没有添加过,重新查询再添加
            //将查询出来的sku信息封装到CartItem中并返回给页面
            CartItemVo cartItemVo = new CartItemVo();

            CompletableFuture<Void> getSkuInfoTask = CompletableFuture.supplyAsync(() -> {
                //调用远程方法查出sku基本信息
                R info = productFeignService.getSkuInfo(skuId);
                //拿到查询出来的skuinfo
                SkuInfoVo skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoVo>() {});
                cartItemVo.setChecked(true);
                cartItemVo.setSkuId(skuId);
                cartItemVo.setCountNum(count);
                cartItemVo.setSkuPrice(skuInfo.getPrice());
                cartItemVo.setSkuDefalutImg(skuInfo.getSkuDefaultImg());
                cartItemVo.setSkuTitle(skuInfo.getSkuTitle());
                cartItemVo.setSkuStock(1);//默认有货
                return skuInfo;
            }, executor).thenAcceptAsync((res)->{
                if (res!=null){
                    //todo 调用库存服务查询商品是否有库存
                    R hasStock = wareFeignService.getHasStock(Arrays.asList(res.getSkuId()));
                    List<SkuStockTo> data = hasStock.getData("data", new TypeReference<List<SkuStockTo>>() {});
                    cartItemVo.setSkuStock(data.get(0).getStock().intValue());
                }

            },executor);

            CompletableFuture<Void> getSaleValuesTask = CompletableFuture.runAsync(() -> {
                //调用远程方法查询sku的销售属性值
                List<String> saleValues =  productFeignService.getSaleValues(skuId);
                cartItemVo.setSkuSaleAttrValues(saleValues);
            }, executor);

            //等待最长执行的异步线程任务完成
            CompletableFuture.allOf(getSkuInfoTask,getSaleValuesTask).get();
            //所有任务完成后，将cartItem转换为json字符串，存入redis
            String jsonString = JSON.toJSONString(cartItemVo);
            //最后将购物的数据保存在redis中
            cartOps.put(skuId.toString(),jsonString);
            return cartItemVo;
        }

       //如果不为空，说明添加的是同一个商品，这样就不再进行查询
        CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId.toString()), CartItemVo.class);
        cartItemVo.setCountNum(cartItemVo.getCountNum()+count);
        //在重新放回redis中
        cartOps.put(skuId.toString(),JSON.toJSONString(cartItemVo));

        return cartItemVo;
    }

    //合并离线购物车和登录购物车
    public void mergeCart(Long skuId, Integer count,CartItemVo cartItem){
        BoundHashOperations<String, String, Object> cartOps = getCartOps();
        Object o = cartOps.get(skuId.toString());
        if (o!=null){
            //购物车中有
            //如果不为空，说明添加的是同一个商品，这样就不再进行查询
            CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId.toString()), CartItemVo.class);
            cartItemVo.setCountNum(cartItemVo.getCountNum()+count);
            //在重新放回redis中
            cartOps.put(skuId.toString(),JSON.toJSONString(cartItemVo));
        }

        //等于空
        cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));

    }



    //通过skuId从redisz中查出保存的sku信息
    @Override
    public CartItemVo getCartItemBySkuId(String skuId) {
        if (skuId!=null){
            BoundHashOperations<String, String, Object> cartOps = getCartOps();
            CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId), CartItemVo.class);

            return cartItemVo;
        }
        return null;
    }

    //获取用户的购物车中的所有商品信息
    @Override
    public CartVo getCart() throws ExecutionException, InterruptedException {

        //判断用户是否登录
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        CartVo cartVo = new CartVo();
        if (userInfoTo.getUserId()!=null){
            //已登录,合并临时用户的购物车和在线的购物车
            List<CartItemVo> tempCartItems = getCartItemsByKey(userInfoTo.getUserKey());
            if (tempCartItems!=null&&tempCartItems.size()>0){
                for (CartItemVo cartItem : tempCartItems) {
//                    cartOps.put(cartItem.getSkuId().toString(),cartItem);
//                    addCart(cartItem.getSkuId(),cartItem.getCountNum());
                    //查出登录后的商品并合并
                    mergeCart(cartItem.getSkuId(),cartItem.getCountNum(),cartItem);
                }
            }

            //获得当前在线用户的已经添加到购物车的信息
            List<CartItemVo> onlineCartItems = getCartItemsByKey(userInfoTo.getUserId().toString());
            cartVo.setItems(onlineCartItems);
            //清空离线购物车
            deleteCartBykey(CartConstant.CART_REDIS_PREFIX+userInfoTo.getUserKey());
            if (onlineCartItems==null){
                //如果在线购物车中没有数据的话直接返回null
                return null;
            }
            return cartVo;
        }
        //没有登录，按照临时用户userkey查出购物信息

        List<CartItemVo> tempCartItems = getCartItemsByKey(userInfoTo.getUserKey());
        if (tempCartItems!=null){
            cartVo.setItems(tempCartItems);
            return cartVo;
        }
        //如果离线购物车中没有数据的话，直接返回null
        return null;
    }

    //根据用户key值或用户id清空购物车
    @Override
    public void deleteCartBykey(String key) {
        stringRedisTemplate.delete(key);
    }


    @Override
    public void updateCartChecked(String skuId, String checked) throws ExecutionException, InterruptedException {
        CartItemVo itemVo = getCartItemBySkuId(skuId);
        if (itemVo!=null){
            itemVo.setChecked(checked.equals("1")?true:false);
            BoundHashOperations<String, String, Object> cartOps = getCartOps();
            cartOps.put(itemVo.getSkuId().toString(),JSON.toJSONString(itemVo));
        }

    }

    @Override
    public void updateCartCount(String skuId, String count) throws ExecutionException, InterruptedException {
        CartItemVo cartItemVo = getCartItemBySkuId(skuId);
        if (Objects.isNull(cartItemVo)){
            return;
        }
        if (Integer.parseInt(count) == 0){
            deleteItemBySkuId(Long.parseLong(skuId));
            return;
        }

        //重新设置商品数量
        cartItemVo.setCountNum(Integer.parseInt(count));
        //重新保存到redis中
        BoundHashOperations<String, String, Object> cartOps = getCartOps();
        cartOps.put(cartItemVo.getSkuId().toString(),JSON.toJSONString(cartItemVo));
    }

    @Override
    public void deleteItemBySkuId(Long skuId) {
        BoundHashOperations<String, String, Object> cartOps = getCartOps();
        CartItemVo cartItemVo = getCartItemBySkuId(skuId.toString());
        if (cartItemVo!=null){
            cartOps.delete(skuId.toString());
        }
    }

    @Override
    public void updateBatchCartChecked(String[] skuIds, String checked) {
        if (skuIds!=null&&skuIds.length>0){
            BoundHashOperations<String, String, Object> cartOps = getCartOps();
            for (String skuId : skuIds) {
                CartItemVo item = getCartItemBySkuId(skuId);
                item.setChecked("1".equals(checked)?true:false);
                cartOps.put(skuId,JSON.toJSONString(item));
            }
        }

    }

    //根据skuIds批量删除购物车里的商品信息
    @Override
    public void deleteBatchItemBySkuId(String[] skuIds) {
        if (skuIds!=null&&skuIds.length>0){
            for (String skuId : skuIds) {
                deleteItemBySkuId(Long.parseLong(skuId));
            }

        }

    }

    @Override
    public List<CartItemVo> getCurrentUserCartItem() throws ExecutionException, InterruptedException {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        if (userInfoTo.getUserId()!=null){
            List<CartItemVo> cartItemVos = getCartItemsByKey(userInfoTo.getUserId().toString());
            if (!CollectionUtils.isEmpty(cartItemVos)){

                List<CartItemVo> cartItemVoList = cartItemVos.stream().filter(item -> item.getChecked()).collect(Collectors.toList());

                    //过滤出已选的购物项,并设置好最新价格，防止用户添加到购物车的数据是几年，保证获取的购物车的数据是最新的
                    cartItemVoList.stream().forEach(item -> {
                        //设置好最新价格
                        item.setSkuPrice(productFeignService.getPriceBySkuId(item.getSkuId()));
                    });

//                CompletableFuture<Void> setStockFuture = CompletableFuture.runAsync(() -> {
//                    List<Long> skuIds = cartItemVoList.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
//                    R hasStock = wareFeignService.getHasStock(skuIds);
//                    if (hasStock.getCode() == 0){
//                        List<SkuStockTo> hasStockData = hasStock.getData("data", new TypeReference<List<SkuStockTo>>() {});
//                        Map<Long, SkuStockTo> skuStockToMap = hasStockData.stream().collect(Collectors.toMap(SkuStockTo::getSkuId, v -> v));
//                        cartItemVoList.stream().forEach(item->{
//                            item.setSkuStock(skuStockToMap.get(item.getSkuId()).getStock().intValue());
//                        });
//                    }
//                },executor);


                return cartItemVoList;
            }

        }
        //没有登录，直接返回null
        return null;
    }

    @Override
    public Integer getCartNum() {
        BoundHashOperations<String, String, Object> cartOps = getCartOps();
        if (cartOps.values()!=null){
            return cartOps.values().size();
        }
        return 0;
    }

    private List<CartItemVo> getCartItemsByKey(String key) {
        BoundHashOperations<String, Object, Object> cartops = stringRedisTemplate.boundHashOps(CartConstant.CART_REDIS_PREFIX + key);
        if (cartops.values()!=null&&cartops.values().size()>0){
            List<CartItemVo> collect = cartops.values().stream().map(item -> {
                CartItemVo cartItemVo = JSON.parseObject(item.toString(), CartItemVo.class);
                return cartItemVo;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;

    }

    private  BoundHashOperations<String, String, Object> getCartOps() {

        BoundHashOperations<String, String, Object> hashOps;
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        //判断用户是否登录，
        if (userInfoTo.getUserId()!=null){
            //用户登录了,为了方便之后访问redis，统一绑定cart的大key
            //key 为：gulimall:cart:(用户id or 临时用户的user-key);
            //TODO 在把绑定用户key的时候需要设置过期时间
            hashOps = stringRedisTemplate.opsForHash().getOperations().boundHashOps(CartConstant.CART_REDIS_PREFIX+userInfoTo.getUserId().toString());
        }else {
            //没有登录,使用系统分配的临时用户user-key作为key
            hashOps = stringRedisTemplate.opsForHash().getOperations().boundHashOps(CartConstant.CART_REDIS_PREFIX+userInfoTo.getUserKey());
        }

        return hashOps;
    }
}
